// Copyright 2017 Xiaomi, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package services

import (
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"path"
	"strconv"
	"strings"

	"github.com/didi/nightingale/src/common/address"
	"github.com/didi/nightingale/src/common/dataobj"
	"github.com/didi/nightingale/src/models"
	"github.com/didi/nightingale/src/modules/agent/config"
	"github.com/didi/nightingale/src/modules/agent/report"
	gcron "github.com/robfig/cron/v3"
	"github.com/toolkits/pkg/logger"
)

type ServiceScheduler struct {
	Cron    *gcron.Cron
	Service *models.ServiceCollect
	Quit    chan struct{}
}

func NewServiceScheduler(service *models.ServiceCollect) *ServiceScheduler {
	scheduler := ServiceScheduler{Service: service}
	scheduler.Cron = gcron.New() //自定义解析器
	scheduler.Quit = make(chan struct{})
	return &scheduler
}

func (c *ServiceScheduler) Schedule() {
	go func() {
		_, err := c.Cron.AddFunc(c.Service.Cron, func() { //注意，这里，跟秒级别有差别
			ServiceRun(c.Service)
		})
		if err != nil {
			logger.Errorf("add service cron job err:%v, job:%v", err, c.Service)
		}
		go c.Cron.Start()
		for {
			select {
			case <-c.Quit:
				c.Cron.Stop()
				return
			}
		}
	}()
}

func (c *ServiceScheduler) Stop() {
	close(c.Quit)
}

func ServiceRun(service *models.ServiceCollect) {
	logger.Infof("service %v %v开始执行", service.Name, service.Id)

	ports := strings.Split(service.Port, ",")
	portints := make([]int, 0, len(ports))
	for _, port := range ports {
		portint, _ := strconv.Atoi(port)
		portints = append(portints, portint)
	}
	ignorePaths := strings.Split(service.IgnorePath, ";")
	addr := address.GetHTTPListen("agent")
	port := strings.Split(addr, ":")[1]
	serviceinfo := dataobj.ServiceInfo{
		Checkid:  service.Id,
		Name:     service.Name,
		Port:     portints,
		Path:     service.ServicePath,
		Data:     service.DataPath,
		Bin:      service.ProcPath,
		Conf:     service.ConfigPath,
		Ignore:   ignorePaths,
		Version:  service.Version,
		AgentUrl: fmt.Sprintf("http://%v:%v/api/collector/service/push", report.IP, port),
		Level:    service.Level,
	}
	sbyte, err := json.Marshal(serviceinfo)
	if err != nil {
		logger.Errorf("json Marshal error, errpr=%v, data=%v", err, serviceinfo)
		return
	}
	currentPath, _ := os.Getwd()
	savePath := path.Join(currentPath, "env", fmt.Sprintf("service_%v", service.Id))
	command := fmt.Sprintf("cd %v && ./service_check '%v' '%v'", config.Config.Stra.EnvScriptPath, string(sbyte), savePath)
	cmd := exec.Command("bash", "-c", command)
	err = cmd.Run()
	if err != nil {
		logger.Errorf("service command=%v exec error, error=%v", command, err)
	}
}
